package com.csw.flink.state

import java.lang
import java.util.Properties

import org.apache.flink.api.common.functions.{RichMapFunction, RuntimeContext}
import org.apache.flink.api.common.serialization.SimpleStringSchema
import org.apache.flink.api.common.state._
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer

object Demo03ListState {
  def main(args: Array[String]): Unit = {

    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment


    //创建配置文件对象
    val properties = new Properties()
    properties.setProperty("bootstrap.servers", "master:9092,node1:9092,node2:9092")
    //    properties.setProperty("group.id", "csw2")
    //    properties.setProperty("auto.offset.reset", "earliest") //earliest：读取所有数据 latest：读取最新数据
    /**
      * 创建kafka消费者
      */
    val consumer: FlinkKafkaConsumer[String] = new FlinkKafkaConsumer[String]("student", new SimpleStringSchema(), properties)

    consumer.setStartFromEarliest()

    val kafkaDS: DataStream[String] = env.addSource(consumer)

    val kvDS: KeyedStream[(String, Int), String] = kafkaDS.map(line => {
      val split: Array[String] = line.split(",")
      (split(4), split(2).toInt)
    }).keyBy(_._1)

    //实时统计每个班级的平均年龄
    val avgAgeDS: DataStream[(String, Double)] = kvDS.map(new ListStateMapFunction)

    avgAgeDS.print()

    env.execute()
  }
}

class ListStateMapFunction extends RichMapFunction[(String, Int), (String, Double)] {

  /**
    * 通过scala的集合也可以实现状态的效果，但是如果任务中途失败了，scala集合中的数据就丢失了
    * flink的状态可以通过checkpoint 保存到hdfs中，如果任务失败后重启，可以恢复到之前的状态
    *
    */
  var listState: ListState[Int] = _

  override def open(parameters: Configuration): Unit = {
    val context: RuntimeContext = getRuntimeContext

    val listStateDesc = new ListStateDescriptor[Int]("list", classOf[Int])

    /**
      * listState 为每一个key保存一个集合
      *
      */
    listState = context.getListState(listStateDesc)
  }

  override def map(value: (String, Int)): (String, Double) = {

    //将每一个年龄保存到状态中
    listState.add(value._2)

    //获取一个班级所有的年龄
    val ages: lang.Iterable[Int] = listState.get()

    //导入一个隐式转换
    import scala.collection.JavaConversions._
    val scalaList: List[Int] = ages.toList

    //计算平均年龄

    val avgAge: Double = scalaList.sum / scalaList.length.toDouble

    //返回数据
    (value._1, avgAge)

  }
}